package cn.sdc.sfw.dao.types;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import cn.sdc.sfw.exception.ReflectException;
import cn.sdc.sfw.util.MethodUtil;
import cn.sdc.sfw.util.ReflectUtil;
import cn.sdc.sfw.util.TypeUtil;

public class DbTypeImpl implements DbType {
	
	private int sqlType;
	private String typeName;
	private Boolean getObjectFlg;
	
	public DbTypeImpl(int sqlType, String typeName, Boolean getObjectFlg) {
		super();
		this.sqlType = sqlType;
		this.typeName = typeName;
		this.getObjectFlg = getObjectFlg;
	}

	@Override
	public Object getValue(ResultSet resultSet, int index) throws SQLException {
		Method method = null;
		Object value;
		try {
			method = ReflectUtil.findMethod(ResultSet.class, 
					getValueMethodName(typeName), new Class[]{int.class});
			value = MethodUtil.invoke(method, resultSet, new Object[]{index});
		} catch (Exception e) {
			e.printStackTrace();
			throw new ReflectException(ReflectException.METHOD_INVOKE_ERR,new Object[]{ResultSet.class, getValueMethodName(typeName)});
		}
		if(getObjectFlg){
			return convertValue(value);
		}else{
			return value;
		}
	}

	@Override
	public Object getValue(ResultSet resultSet, String columnName)
			throws SQLException {
		Method method = null;
		Object value;
		try {
			method = ReflectUtil.findMethod(ResultSet.class, 
					getValueMethodName(typeName), new Class[]{String.class});
			value = MethodUtil.invoke(method, resultSet, new Object[]{columnName});
		} catch (Exception e) {
			e.printStackTrace();
			throw new ReflectException(ReflectException.METHOD_INVOKE_ERR,new Object[]{ResultSet.class, getValueMethodName(typeName)});
		}
		if(getObjectFlg){
			return convertValue(value);
		}else{
			return value;
		}
	}

	@Override
	public void bindValue(PreparedStatement ps, int index, Object value)
			throws SQLException {
		
		if (value == null) {
			ps.setNull(index, sqlType);
		}else if(sqlType == Types.BINARY){
			if (value instanceof byte[]) {
				byte[] ba = (byte[]) value;
				InputStream in = new ByteArrayInputStream(ba);
				ps.setBinaryStream(index, in, ba.length);
			} else {
				ps.setObject(index, value);
			}
		}else{
			try {
				Method method = ReflectUtil.findMethod(PreparedStatement.class, 
						getSetMethodName(typeName),new Class[]{int.class,DbTypes.getJavaClass(sqlType)});
				MethodUtil.invoke(method, ps, new Object[]{index,convertValue(value)});
			} catch (Exception e) {
				e.printStackTrace();
				throw new ReflectException(ReflectException.METHOD_INVOKE_ERR,new Object[]{PreparedStatement.class, getSetMethodName(typeName)});
			}
		}

	}
	
	private Object convertValue(Object value){
		Method method = ReflectUtil.findMethod(TypeUtil.class, 
				getConvMethodName(typeName), new Class[]{Object.class});
		Object result = null;
		try {
			result = method.invoke(TypeUtil.class, new Object[]{value});
		} catch (Exception e) {
			e.printStackTrace();
			throw new ReflectException(ReflectException.METHOD_INVOKE_ERR,new Object[]{TypeUtil.class, getConvMethodName(typeName)});
		}
		return result;
	}
	
	private String getValueMethodName(String typeName){
		if(getObjectFlg){
			return "getObject";
		}else{
			return "get" + typeName.substring(0, 1).toUpperCase() + typeName.substring(1);
		}
	}
	private String getConvMethodName(String typeName){
		if(typeName.equals("date")){
			return "toSqlDate";
		}else{
			return "to" + typeName.substring(0, 1).toUpperCase() + typeName.substring(1);
		}
	}
	private String getSetMethodName(String typeName){
		if(typeName.equals("integer")){
			return "setInt";
		}else{
			return "set" + typeName.substring(0, 1).toUpperCase() + typeName.substring(1);
		}
	}

}
